﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Diagnostics;

namespace LowLevelGraphics.ColorSpaces
{
    /// <summary>
    /// HSV Channel
    /// </summary>
    public enum HSVChannel
    {
        /// <summary>
        /// 
        /// </summary>
        H = 0,
        /// <summary>
        /// 
        /// </summary>
        S = 1,
        /// <summary>
        /// 
        /// </summary>
        V = 2
    }

    /// <summary>
    /// 
    /// </summary>
    [DebuggerDisplay("H: {m_H}, S: {m_S}, V: {m_V}")]
    public class HSV : AbstractColorSpace
    {
        protected double m_H = 0.0f;
        protected double m_S = 0.0f;
        protected double m_V = 0.0f;

        /// <summary>
        /// primary constructor
        /// </summary>
        /// <param name="_color"></param>
        public HSV(Color _color)
            : base(_color)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HSV"/> class.
        /// </summary>
        /// <param name="_H">The _ H.</param>
        /// <param name="_S">The _ S.</param>
        /// <param name="_V">The _ V.</param>
        public HSV(double _H, double _S, double _V)
        {
            m_H = _H;
            m_S = _S;
            m_V = _V;
        }

        /// <summary>
        /// used by clone to create a deep copy
        /// </summary>
        /// <param name="_hsv"></param>
        internal HSV(HSV _hsv)
        {
            this.m_H = _hsv.m_H;
            this.m_S = _hsv.m_S;
            this.m_V = _hsv.m_V;
        }

        /// <summary>
        /// Enable casting into operator
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static explicit operator HSV(Color _color)
        {
            return new HSV(_color);
        }

        /// <summary>
        /// Gets or sets H
        /// </summary>
        public double H
        {
            get { return m_H; }
            set { m_H = value; }
        }

        /// <summary>
        /// Gets or sets S
        /// </summary>
        public double S
        {
            get { return m_S; }
            set { m_S = value; }
        }

        /// <summary>
        /// Gets or sets V
        /// </summary>
        public double V
        {
            get { return m_V; }
            set { m_V = value; }
        }

        /// <summary>
        /// Used to transform color into this color space
        /// </summary>
        protected override void CalcValues()
        {
            //float min, max, delta;
            int r = m_Color.R;
            int g = m_Color.G;
            int b = m_Color.B;
            int min = Math.Min(r, Math.Min(g, b));
            int max = Math.Max(r, Math.Max(g, b));
            int v = max;				// v
            int s = 0;
            int delta = max - min;
            if (delta == 0) delta = 1;
            int h = 0;
            if (max != 0)
                s = delta / max;		// s
            else
            {
                // r = g = b = 0		// s = 0, v is undefined
                s = 0;
                h = -1;
                return;
            }

            if (r == max)
                h = (g - b) / delta;		// between yellow & magenta
            else if (g == max)
                h = 2 + (b - r) / delta;	// between cyan & yellow
            else
                h = 4 + (r - g) / delta;	// between magenta & cyan

            h *= 60;				// degrees
            if (h < 0)
                h += 360;

            m_H = h;
            m_S = s;
            m_V = v;
        }

        /// <summary>
        /// transform back
        /// </summary>
        /// <returns></returns>
        public override Color ToColor()
        {
            int i;
            double f, p, q, t;
            double r, g, b;

            double h = m_H;
            double s = m_S;
            double v = m_V;

            if (s == 0)
            {
                //achromatic(grey);
                 r = g = b = (int)v;
                return Color.FromArgb((int)r,(int)g,(int)b);
            }

                h /= 60;			// sector 0 to 5
                i = (int)Math.Floor( h );
                f = h - i;			// factorial part of h
                p = v * ( 1 - s );
                q = v * ( 1 - s * f );
                t = v * ( 1 - s * ( 1 - f ) );

                switch( i ) {
                    case 0:
                        r = v;
                        g = t;
                        b = p;
                        break;

                    case 1:
                        r = q;
                        g = v;
                        b = p;
                        break;

                    case 2:
                        r = p;
                        g = v;
                        b = t;
                        break;

                    case 3:
                        r = p;
                        g = q;
                        b = v;
                        break;

                    case 4:
                        r = t;
                        g = p;
                        b = v;
                        break;

                    default:		// case 5:
                        r = v;
                        g = p;
                        b = q;
                        break;
                }    
            return Color.FromArgb((int)r, (int)g, (int)b);
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("Color H = {0}, S = {1}, V = {2}", m_H, m_S, m_V);
        }

        /// <summary>
        /// Gets the values.
        /// </summary>
        /// <returns></returns>
        public override object[] GetValues()
        {
            return new object[] { m_H, m_S, m_V };
        }

        /// <summary>
        /// Sets the values.
        /// </summary>
        /// <param name="_aObject">The _a object.</param>
        public override void SetValues(object[] _aObject)
        {
            this.m_H = (double)_aObject[0];
            this.m_S = (double)_aObject[1];
            this.m_V = (double)_aObject[2];
        }

        /// <summary>
        /// used to create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new HSV(this);
        }
    }
}
